package com.hyt.it.ogt.kq.service.gov.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Sets;
import com.hyt.common.apiInfor.ApiResponse;
import com.hyt.core.base.BaseEntity;
import com.hyt.core.util.UUIDUtils;
import com.hyt.it.ogt.kq.common.config.exception.KqException;
import com.hyt.it.ogt.kq.common.gov.constant.ResponseCode;
import com.hyt.it.ogt.kq.service.gov.mapper.ExaminerMapper;
import com.hyt.it.ogt.kq.service.gov.model.entity.*;
import com.hyt.it.ogt.kq.service.gov.model.param.proctor.ManualProctorParam;
import com.hyt.it.ogt.kq.service.gov.model.param.proctor.ProctorParam;
import com.hyt.it.ogt.kq.service.gov.model.param.proctor.ProctorRoomSearchParam;
import com.hyt.it.ogt.kq.service.gov.model.vo.ExamRoomTypeVo;
import com.hyt.it.ogt.kq.service.gov.model.vo.examiner.*;
import com.hyt.it.ogt.kq.service.gov.service.*;
import com.hyt.it.ogt.kq.service.gov.service.obs.CloudObsService;
import com.hyt.it.ogt.kq.service.model.entity.Subject;
import com.hyt.it.ogt.kq.service.model.entity.TaskCandidateArea;
import com.hyt.loginfo.model.Tree;
import com.hyt.loginfo.model.UserVO;
import com.hyt.loginfo.model.param.QueryUsersParam;
import com.hyt.loginfo.service.IUaDeptService;
import com.hyt.loginfo.service.IUaUserExtService;
import com.hyt.progress.service.ProgressManager;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.File;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;


@Slf4j
@Service
public class ProctorServiceImpl implements IProctorService {
    @Resource
    private ExaminerMapper examinerMapper;
    @Resource
    private IUaUserExtService iUaUserExtService;
    @Resource
    private IUaDeptService iUaDeptService;
    @Resource
    private ITimeExaminerService iTimeExaminerService;
    @Resource
    private ITimeRoomExaminerService iTimeRoomExaminerService;
    @Resource
    private ITimeRoomService iTimeRoomService;
    @Resource
    private ITaskService iTaskService;
    @Resource
    private ISubjectService iSubjectService;
    @Resource
    private ITimeRoomCandidateService iTimeRoomCandidateService;
    @Resource
    private CloudObsService cloudObsService;
    @Resource
    private IExaminerService iExaminerService;
    @Resource
    private ITaskPlaceService iTaskPlaceService;
    @Resource
    private ITaskAreaService iTaskAreaService;
    @Resource
    private ITaskCandidateAreaService iTaskCandidateAreaService;

    @Override
    public ProctorArrangeResultVO getProctorArrangeResult(String taskId) {
        // 查询监考员总数量
        Integer count = iExaminerService.lambdaQuery().eq(Examiner::getTaskId, taskId).eq(BaseEntity::getDelFlag, false).count();

        // 查询已编排监考员数量
        List<TimeRoomExaminer> proctorList = iTimeRoomExaminerService.lambdaQuery().select(BaseEntity::getId).eq(TimeRoomExaminer::getTaskId, taskId).eq(BaseEntity::getDelFlag, false).groupBy(TimeRoomExaminer::getUserId).list();

        // 查询有监考员考场数量
        List<TimeRoomExaminer> roomProctorList = iTimeRoomExaminerService.lambdaQuery().select(BaseEntity::getId).eq(TimeRoomExaminer::getTaskId, taskId).eq(BaseEntity::getDelFlag, false).groupBy(TimeRoomExaminer::getRoomId).list();

        // 查询考场数量
        List<TimeRoom> roomList = iTimeRoomService.lambdaQuery().select(BaseEntity::getId).eq(TimeRoom::getTaskId, taskId).eq(BaseEntity::getDelFlag, false).groupBy(TimeRoom::getRoomId).list();

        // 查询科目总数量
        Integer subjectCount = iSubjectService.lambdaQuery().eq(Subject::getTaskId, taskId).eq(BaseEntity::getDelFlag, false).count();

        // 查询已编排科目数量
        List<TimeRoomCandidate> subjectList = iTimeRoomCandidateService.lambdaQuery().select(BaseEntity::getId).eq(TimeRoomCandidate::getTaskId, taskId).eq(BaseEntity::getDelFlag, false).groupBy(TimeRoomCandidate::getSubjectId).list();

        return ProctorArrangeResultVO.builder().proctorNum(count).arrangedProctorNum(proctorList.size())
                .roomNum(roomList.size()).roomProctorNum(roomProctorList.size()).subjectNum(subjectCount)
                .arrangedSubjectNum(subjectList.size()).proctorArrangeDate(iTaskService.getById(taskId).getProctorArrangeDate()).build();
    }

    /**
     * 获取指定部门及以下部门的ID
     * @param deptId
     * @return
     */
    @Override
    public Set<String> getAncestorsByDeptId(String deptId) {
        if(StringUtils.isBlank(deptId)) {
            return null;
        }
        Set<String> sets = Sets.newHashSet();
        sets.add(deptId);
        List<Tree> officeTree = iUaDeptService.getOfficeTree();
        List<String> nodeAndChildrenIds = this.findNodeAndChildrenIds(officeTree.get(0), deptId);
        sets.addAll(nodeAndChildrenIds);
        return sets;
    }

    /**
     * 递归循环，获取指定部门及以下部门的ID
     * @param node
     * @param id
     * @return
     */
    public List<String> findNodeAndChildrenIds(Tree node, String id) {
        List<String> result = new ArrayList<>();

        // 如果当前节点的ID匹配目标ID，将其ID添加到结果列表中
        if (StrUtil.isNotBlank(node.getPid()) && node.getPid().equals(id)) {
            result.add(node.getId());
            if(node.getChildren() != null && node.getChildren().size() > 0) {
                // 递归地搜索子节点
                for (Tree child : node.getChildren()) {
                    List<String> childIds = findNodeAndChildrenIds(child, node.getId());
                    result.addAll(childIds);
                }
            }
        } else {
            if (node.getChildren() != null && node.getChildren().size() > 0) {
                // 递归地搜索子节点
                for (Tree child : node.getChildren()) {
                    List<String> childIds = findNodeAndChildrenIds(child, id);
                    result.addAll(childIds);
                }
            }
        }
        return result;
    }

    /**
     * 获取本节点以下的一个parentID值，用于like查询
     * @param taskId
     * @param areaId
     * @return
     */
    @Override
    public String getAreasByAreaId(String taskId, String areaId) {
        StringBuffer sb = new StringBuffer();
        if (StringUtils.isNotBlank(areaId)) {
            // 查询一个子节点
            TaskArea one = iTaskAreaService.lambdaQuery().eq(TaskArea::getTaskId, taskId)
                    .eq(TaskArea::getAreaId, areaId).eq(BaseEntity::getDelFlag, false).last("limit 1").one();
            if (one != null) {
                sb.append(one.getParentIds());
                sb.append(",");
                sb.append(areaId);
                sb.append(",");
            }
        }
        return sb.length() > 0 ? sb.toString() : null;
    }

    /**
     * 获取本节点以下的一个parentID值，用于like查询
     * @param taskId
     * @param areaId
     * @return
     */
    @Override
    public String getCandidateAreasByAreaId(String taskId, String areaId) {
        StringBuffer sb = new StringBuffer();
        if (StringUtils.isNotBlank(areaId)) {
            TaskCandidateArea one = iTaskCandidateAreaService.lambdaQuery().eq(TaskCandidateArea::getTaskId, taskId)
                    .eq(TaskCandidateArea::getParentId, areaId).eq(BaseEntity::getDelFlag, false).last("limit 1").one();
            if (one != null) {
                sb.append(one.getParentIds());
            }
        }
        return sb.length() > 0 ? sb.toString() : null;
    }


    @Override
    public Page<ProctorArrangeRoomVO> getProctorRoom(Page<ProctorArrangeRoomVO> page, ProctorRoomSearchParam param) {
        //设置父节点字符
        param.setDeptIds(this.getAncestorsByDeptId(param.getDept()));

        if (param.getArrangeState() == 1) { // 查询已编排的监考员
            examinerMapper.getProctorRoom(page, param);
        } else if (param.getArrangeState() == 0) { // 查询未编排监考员
            examinerMapper.getProctorRoomNotArrange(page, param);
        } else if (param.getArrangeState() == 2) { // 查询所有监考员
            examinerMapper.getAllProctorRoom(page, param);
        }
        if(CollectionUtils.isNotEmpty(page.getRecords())) {
            List<String> deptIds = page.getRecords().stream()
                    .filter(s -> StringUtils.isNotBlank(s.getDept())).map(ProctorArrangeRoomVO::getDept).collect(Collectors.toList());
            // 先从平台拿到机构名称
            Map<String, String> ancestorsNames = new HashMap<>();
            if(deptIds.size() > 0) {
                ancestorsNames.putAll(iUaDeptService.getAncestorsNames(deptIds));
            }


            List<ExamRoomTypeVo> types = iSubjectService.queryExamRoomTYpes();
            // 获取所有考场类型定义
            Map<String, ExamRoomTypeVo> typeMap = types.stream()
                    .collect(Collectors.toMap(ExamRoomTypeVo::getId, Function.identity(), (k1, k2) -> k1));
            page.getRecords().forEach(item -> {
                if (!StringUtils.isEmpty(item.getDept())) {
                    item.setDept(ancestorsNames.get(item.getDept()));
                }
                // 考场考多少个科目
                String[] subjectArr = item.getSubjectStr().split(",");
                StringBuffer sb = new StringBuffer();
                for (String s : subjectArr) {
                    sb.append(",");
                    sb.append(iSubjectService.getById(s).getName());
                }
                // 拼接科目名称
                item.setSubjectStr(sb.substring(1));

                if (param.getArrangeState() == 1 || param.getArrangeState() == 2) { // 已编排或者所有的监考员
                    // 获取当前场次下的监考员列表
                    List<String> examinerList = examinerMapper.getExaminerList(param.getTaskId(), item.getTimeId(), item.getRoomId());
                    if (CollectionUtils.isNotEmpty(examinerList)) {
                        List<ProctorCommonResponseTemplateVO> proctorList = new ArrayList<>();
                        // 循环监考员，拿到监考员的id和名称
                        examinerList.stream().forEach(id -> {
                            QueryUsersParam prams = new QueryUsersParam();
                            prams.setUserids(Arrays.asList(id));
                            ApiResponse<List<UserVO>> usersByIds = iUaUserExtService.getUsersByIds(prams);
                            if (Objects.nonNull(usersByIds.getData()) && usersByIds.getData().size() > 0) {
                                UserVO user = usersByIds.getData().get(0);
                                proctorList.add(ProctorCommonResponseTemplateVO.builder().id(user.getUserid()).name(user.getUsername()).build());
                            }
                        });
                        item.setProctorList(proctorList);
                    }
                    if (StringUtils.isNotBlank(item.getRoomType())) {
                        // 设置考场类型
                        ExamRoomTypeVo examRoomTypeVo = typeMap.get(item.getRoomType());
                        if (examRoomTypeVo != null) {
                            item.setRoomTypeName(examRoomTypeVo.getTypeName());
                        }
                    }
                }
            });
        }
        return page;
    }


    @Override
    public void checkProctorLockState(String taskId){
        // 0：判断考生编排数据是否锁定
        Task task = iTaskService.getById(taskId);
        if(task.getPublish()){
            throw new KqException(ResponseCode.TASK_PUBLISH_TRUE.getCode(), "任务已发布，不允许操作此功能。");
        }
        if(task.getProctorDataLock()) {
            throw new KqException(ResponseCode.PROCTOR_DATA_LOCK_FALSE.getCode(), "监考员编排已锁定，不允许操作此功能。");
        }

    }

    @Override
    public void checkLockState(String taskId){
        // 0：判断考生编排数据是否锁定
        Task task = iTaskService.getById(taskId);
        if(task.getPublish()){
            throw new KqException(ResponseCode.TASK_PUBLISH_TRUE.getCode(), "任务已发布，不允许操作此功能。");
        }
        if (!task.getDataLock()) {
            throw new KqException(ResponseCode.TASK_DATA_LOCK_FALSE.getCode(), "考生数据没有锁定，不允许操作此功能。");
        } else if(task.getProctorDataLock()) {
            throw new KqException(ResponseCode.PROCTOR_DATA_LOCK_FALSE.getCode(), "监考员编排已锁定，不允许操作此功能。");
        }

    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void manualProctorArrange(ManualProctorParam param) {
        // 校验
        this.checkLockState(param.getTaskId());

        List<TimeExaminer> timeExaminerList = new ArrayList<>();
        List<TimeRoomExaminer> timeRoomExaminerList = new ArrayList<>();

        // 遍历监考员，设置对应表数据
        param.getExaminerIds().forEach(s -> {
            // 批次监考员表（多余的表，老逻辑不动了）
            TimeExaminer timeExaminer = new TimeExaminer();
            timeExaminer.setExaminerId(s).setTaskId(param.getTaskId()).setTimeId(param.getTimeId())
                    .setUserId(iExaminerService.getById(s).getUserId()).setId(UUIDUtils.randomUUID());
            timeExaminerList.add(timeExaminer);

            // 场次监考员表
            TimeRoomExaminer timeRoomExaminer = new TimeRoomExaminer();
            timeRoomExaminer.setTaskId(param.getTaskId()).setTimeExaminerId(timeExaminer.getId())
                    .setTimeId(param.getTimeId()).setRoomId(param.getRoomId()).setUserId(timeExaminer.getUserId())
                    .setId(UUIDUtils.randomUUID());
            timeRoomExaminerList.add(timeRoomExaminer);
        });


        if (timeExaminerList.size() > 0) {
            param.getExaminerIds().forEach(s -> {
            iTimeExaminerService.lambdaUpdate().set(BaseEntity::getDelFlag, true).eq(TimeExaminer::getTaskId, param.getTaskId())
                    .eq(TimeExaminer::getTimeId, param.getTimeId()).eq(TimeExaminer::getExaminerId,s).eq(BaseEntity::getDelFlag, false).update();
            });
            iTimeExaminerService.saveBatch(timeExaminerList);
        }

        if (timeRoomExaminerList.size() > 0) {
            iTimeRoomExaminerService.lambdaUpdate().set(BaseEntity::getDelFlag, true).eq(TimeRoomExaminer::getTaskId, param.getTaskId())
                    .eq(TimeRoomExaminer::getTimeId, param.getTimeId()).eq(TimeRoomExaminer::getRoomId, param.getRoomId()).eq(BaseEntity::getDelFlag, false).update();
            iTimeRoomExaminerService.saveBatch(timeRoomExaminerList);
            iTaskService.lambdaUpdate().set(Task::getProctorArrangeDate, LocalDateTime.now()).eq(BaseEntity::getId, param.getTaskId()).update();
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void resetArrange(String taskId) {
        // 之所以多开一个方法，是因为该逻辑被其他方法重用，为了避免事务再开启内部事务
        this.resetProctorArrange(taskId);
    }

    @Override
    public void resetProctorArrange(String taskId) {
        iTimeExaminerService.deleteByTask(taskId);
        iTimeRoomExaminerService.deleteByTask(taskId);
        iTaskService.lambdaUpdate().set(Task::getProctorArrangeType, 0).eq(BaseEntity::getId, taskId).update();
    }

    @Override
    public List<TimeRoomParamVO> getProctorParam(String taskId) {
        return examinerMapper.getProctorParam(taskId);
    }

    @Override
    public List<ProctorVO> getProctor(ProctorParam param) {
        List<ProctorVO> proctorVOS = new ArrayList<>();
        List<Examiner> list = iExaminerService.lambdaQuery().eq(Examiner::getTaskId, param.getTaskId()).eq(BaseEntity::getDelFlag, false).list();
        Map<String, TimeRoomExaminer> map = iTimeRoomExaminerService.lambdaQuery().eq(TimeRoomExaminer::getTaskId, param.getTaskId())
                .eq(TimeRoomExaminer::getTimeId, param.getTimeId()).eq(TimeRoomExaminer::getRoomId, param.getRoomId())
                .eq(BaseEntity::getDelFlag, false).list().stream().collect(Collectors.toMap(TimeRoomExaminer::getUserId, Function.identity()));
        list.stream().forEach(item -> {
            ProctorVO vo = new ProctorVO();
            QueryUsersParam prams = new QueryUsersParam();
            prams.setUserids(Arrays.asList(item.getUserId()));
            vo.setName(iUaUserExtService.getUsersByIds(prams).getData().get(0).getUsername());
            vo.setId(item.getId());
            vo.setSelected(map.containsKey(item.getUserId()));
            proctorVOS.add(vo);
        });
        return proctorVOS.stream().sorted(Comparator.comparing(ProctorVO::getSelected).reversed()).collect(Collectors.toList());
    }

    @Override
    public List<ProctorCommonResponseTemplateVO> getPlace(String taskId, int arrangeState) {
        if (arrangeState == 2) {
            // 已参与考生编排的考点
            return examinerMapper.getPlace(taskId);
        } else {
            List<ProctorCommonResponseTemplateVO> place = examinerMapper.getProctorPlace(taskId);
            if (arrangeState == 1) {
                // 已编排监考员的考点
                return place;
            } else {
                // 未编排监考员的考点
                List<ProctorCommonResponseTemplateVO> placeList = new ArrayList<>();
                List<TaskPlace> list = iTaskPlaceService.lambdaQuery().eq(TaskPlace::getTaskId, taskId)
                        .eq(BaseEntity::getDelFlag, false).orderByAsc(TaskPlace::getCode).list();
                Map<String, ProctorCommonResponseTemplateVO> collect = place.stream().collect(Collectors.toMap(ProctorCommonResponseTemplateVO::getId, Function.identity()));
                list.stream().forEach(item -> {
                    if (!collect.containsKey(item.getId())) {
                        placeList.add(ProctorCommonResponseTemplateVO.builder().id(item.getId()).name(item.getName()).build());
                    }
                });
                return placeList;
            }
        }
    }

    @Override
    public List<ProctorCommonResponseTemplateVO> getRoom(String taskId, String placeId, int arrangeState) {
        List<ProctorCommonResponseTemplateVO> room = examinerMapper.getRoom(taskId, placeId);
        if (arrangeState == 2) {
            return room;
        } else {
            List<TimeRoomExaminer> list = iTimeRoomExaminerService.lambdaQuery().eq(TimeRoomExaminer::getTaskId, taskId)
                    .eq(BaseEntity::getDelFlag, false).groupBy(TimeRoomExaminer::getRoomId).list();
            Map<String, ProctorCommonResponseTemplateVO> collect = room.stream().collect(Collectors.toMap(ProctorCommonResponseTemplateVO::getId, Function.identity()));
            if (arrangeState == 1) {
                List<ProctorCommonResponseTemplateVO> result = new ArrayList<>();
                list.stream().forEach(item -> {
                    ProctorCommonResponseTemplateVO proctorCommonResponseTemplateVO = collect.get(item.getRoomId());
                    if(proctorCommonResponseTemplateVO != null) {
                        result.add(proctorCommonResponseTemplateVO);
                    }
                });
                return result;
            } else {
                list.stream().forEach(item -> {
                    collect.remove(item.getRoomId());
                });
                return new ArrayList<>(collect.values());
            }
        }
    }

    @Override
    public List<ProctorCommonResponseTemplateVO> getTime(String taskId, int arrangeState) {
        List<ProctorCommonResponseTemplateVO> time = examinerMapper.getTime(taskId);
        if (arrangeState == 2) {
            return time;
        } else {
            List<TimeRoomExaminer> list = iTimeRoomExaminerService.lambdaQuery().eq(TimeRoomExaminer::getTaskId, taskId)
                    .eq(BaseEntity::getDelFlag, false).groupBy(TimeRoomExaminer::getTimeId).list();
            Map<String, ProctorCommonResponseTemplateVO> collect = time.stream().collect(Collectors.toMap(ProctorCommonResponseTemplateVO::getId, Function.identity()));
            if (arrangeState == 1) {
                List<ProctorCommonResponseTemplateVO> result = new ArrayList<>();
                list.stream().forEach(item -> {
                    ProctorCommonResponseTemplateVO proctorCommonResponseTemplateVO = collect.get(item.getTimeId());
                    if(proctorCommonResponseTemplateVO != null) {
                        result.add(proctorCommonResponseTemplateVO);
                    }
                });
                return result;
            } else {
                list.stream().forEach(item -> {
                    collect.remove(item.getRoomId());
                });
                return new ArrayList<>(collect.values());
            }
        }
    }

    @SneakyThrows
    @Override
    public void downloadProctorArrange(String taskId) {
        List<ProctorArrangeRoomVO> dataList = new ArrayList<>();
        int current = 1;
        boolean flag = true;
        ProgressManager.updateProgressCurrent(1L, "开始准备数据");
        ProctorRoomSearchParam param = new ProctorRoomSearchParam();
        param.setTaskId(taskId);
        while (flag) {
            Page<ProctorArrangeRoomVO> page = new Page<>(current, 2000);
            this.getProctorRoom(page, param);
            if (page.getRecords().size() == 0) {
                flag = false;
            } else {
                page.getRecords().stream().forEach(item -> {
                    StringBuffer sb = new StringBuffer();
                    sb.append(",");
                    item.getProctorList().stream().forEach(p -> {
                        sb.append(p.getName());
                    });
                    item.setProctorNames(sb.substring(1).toString());
                });
                dataList.addAll(page.getRecords());
                current++;
            }
        }
        ProgressManager.updateProgressCurrent(50L, "数据准备完成");
        String lastFileName = DateUtil.now() + ".xlsx";
        File tempFile = FileUtil.createTempFile(StrPool.DOT + ".xlsx", Boolean.TRUE);
        tempFile.deleteOnExit();
        ProgressManager.updateProgressCurrent(55L, "Excel生成中");
        EasyExcel.write(tempFile, ProctorArrangeRoomVO.class).sheet(0).doWrite(dataList);
        String path = "kw-gov/proctor/" + lastFileName;
        String fileUrl = cloudObsService.updateFile(path, tempFile);
        log.debug("# excel文件上传华为云成功：{}，{}", lastFileName, fileUrl);
        ProgressManager.updateProgressCurrent(99L, "Excel已生成");
        ProgressManager.updateProgressCurrent(100L, fileUrl);
    }

    @Override
    public void lockProctorArrange(String taskId, boolean lockState) {
        List<TimeRoomExaminer> list = iTimeRoomExaminerService.lambdaQuery().eq(TimeRoomExaminer::getTaskId, taskId).eq(BaseEntity::getDelFlag, false).list();
        if(CollectionUtil.isEmpty(list)){
            throw new KqException(ResponseCode.PROCTOR_ARRANGE_NO_DATA.getCode(), "没有监考员编排数据，无法锁定编排。");
        }
        iTaskService.lambdaUpdate().set(Task::getProctorDataLock, lockState).eq(BaseEntity::getId, taskId).update();
    }

    @Override
    public void initProctorParam(String taskId) {
        examinerMapper.initProctorParam(taskId);
    }
}
